package cz.possoft.explorer.meta.builder;

import java.io.BufferedInputStream;
import java.util.ArrayList;

import org.jibx.runtime.BindingDirectory;
import org.jibx.runtime.IBindingFactory;
import org.jibx.runtime.IUnmarshallingContext;

import cz.possoft.explorer.meta.DomainMetadata;
import cz.possoft.explorer.resolver.Resolver;
import cz.possoft.explorer.resolver.method.MethodResolver;
import cz.possoft.explorer.resolver.method.MethodResolverImpl;

/**
 * Initialize domain metadata from XML file and it internally uses JiBX to parse XML.<br>
 * Default location of XML file with domain configuration is "conf/domain.xml" where conf should be in classpath.
 * Different value can specified by system property "domain.metadata.xml.file" .
 *  
 * @author <a href="mailto:mposolda@gmail.com">Marek Posolda</a>
 * @version $Revision$
 */
public class XMLDomainMetadataBuilderImpl implements DomainMetadataBuilder
{
   // Name of system property with path to domain XML file
   private static final String PROPERTY_XML = "domain.metadata.xml.file";
   
   // Default value of xml file if property XML_NAME is not specified in system properties.
   private static final String DEFAULT_XML_VALUE = "conf/domain.xml";
   
   public DomainMetadata build() throws DomainMetadataException
   {
      try
      {                  
         // obtain XML configuration with usage of JiBX
         String path = System.getProperty(PROPERTY_XML) != null ? System.getProperty(PROPERTY_XML) : DEFAULT_XML_VALUE;
         IBindingFactory bfact = BindingDirectory.getFactory(XMLData.class);
         IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
         XMLData xmlConfig = (XMLData)uctx.unmarshalDocument(new BufferedInputStream(Thread.currentThread().getContextClassLoader().getResourceAsStream(path)), null);                  
         
         // create instance of DomainMetadata based on requested DomainMetadata class

         // this should be type-safe
         Class<? extends DomainMetadata> domainMetadataClass = (Class<? extends DomainMetadata>)Class.forName(xmlConfig.domainMetadataClassName);
         DomainMetadata resultMetadata = domainMetadataClass.newInstance();
         
         // iterate over all properties from property files
         for (XMLData.XMLClasses.XMLClass xmlClass  : xmlConfig.xmlClasses.classes)
         {
            String typeClassName = xmlClass.className;
            // get resolver class name
            String resolverClassName = xmlClass.resolverClassName;
         
            // get metadata classname .
            String metadataClassName = xmlClass.metadataClassName;
            Class metadataClass = Class.forName(metadataClassName);
            
            // get method resolver classname. Use default value if methodResolverClassName is not set.
            String methodResolverClassName = xmlClass.methodResolverClassName;
            Class<? extends MethodResolver> methodResolverClass = methodResolverClassName == null ? MethodResolverImpl.class : (Class<? extends MethodResolver>)Class.forName(methodResolverClassName);
            
            // load classes and create instance of Resolver
            Class typeClass = Class.forName(typeClassName);
            Class<? extends Resolver<?,?>> resolverClass = (Class<? extends Resolver<?,?>>)Class.forName(resolverClassName); 
            Resolver resolver = resolverClass.newInstance();
            MethodResolver methodResolver = methodResolverClass.newInstance();
         
            // TODO: we don't know the type of typeClass :-( Maybe it is possible to do it more type-safe. It needs investigation.
            resultMetadata.addClassWithAllSupertypes(typeClass, resolver, metadataClass, methodResolver);
         } 
         
         return resultMetadata;
      }
      catch (DomainMetadataException e)
      {
         throw e;
      }
      catch (Exception e)
      {
         throw new DomainMetadataException(e);
      }
   }
   
   private static class XMLData
   {
      private XMLClasses xmlClasses;   
      private String domainMetadataClassName;
      
      private static class XMLClasses
      {
         private ArrayList<XMLClass> classes;   
         
         private static class XMLClass
         {
            private String className;
            private String resolverClassName;
            private String metadataClassName;
            private String methodResolverClassName;
         }
      }

   }   

}

